using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xna.Framework;

namespace HappySprite.Tests
{
    [TestClass]
    public class RotationTests
    {
        [TestMethod]
        public void DefaultConstructorTest()
        {
            Rotation rotation = new Rotation();
            // Default constructor should create an identity rotation
            Assert.AreEqual(Quaternion.Identity, rotation.Quaternion);
        }

        [TestMethod]
        public void QuaternionConstructorTest()
        {
            Quaternion q = Quaternion.CreateFromYawPitchRoll(
                MathHelper.ToRadians(10),
                MathHelper.ToRadians(20),
                MathHelper.ToRadians(30));

            Rotation rotation = new Rotation(q);

            Assert.AreEqual(q, rotation.Quaternion);
        }

        [TestMethod]
        public void AxisAngleConstructorTest()
        {
            Vector3 axis = Vector3.Left;
            float angle = 1f;

            Rotation rotation = new Rotation(axis, angle);

            Assert.AreEqual(axis, rotation.Axis);
            Assert.AreEqual(angle, rotation.Angle);
        }

        [TestMethod]
        public void YawPitchRollConstructorTest()
        {
            float yaw = MathHelper.ToRadians(10);
            float pitch = MathHelper.ToRadians(20);
            float roll = MathHelper.ToRadians(30);

            Rotation rotation = new Rotation(yaw, pitch, roll);

            Assert.AreEqual(yaw, rotation.Yaw);
            Assert.AreEqual(pitch, rotation.Pitch);
            Assert.AreEqual(roll, rotation.Roll);
        }

        [TestMethod]
        public void AxisAngleTest()
        {
            Rotation rotation = new Rotation();

            // Ensure Axis defaults to z-axis
            rotation.Angle = MathHelper.ToRadians(10f);
            Quaternion q = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, MathHelper.ToRadians(10f));
            Assert.AreEqual(q, rotation.Quaternion);

            // Change axis -- rotation stays the same
            q = Quaternion.CreateFromAxisAngle(new Vector3(1f, 1f, 1f), MathHelper.ToRadians(10f));
            rotation.Axis = new Vector3(1f, 1f, 1f);
            Assert.AreEqual(q, rotation.Quaternion);

            // Rotate angle using += operator
            q = Quaternion.CreateFromAxisAngle(new Vector3(1f, 1f, 1f), MathHelper.ToRadians(20f));
            rotation.Angle += MathHelper.ToRadians(10);
            Assert.AreEqual(q, rotation.Quaternion);

            // Changing quaternion updates Axis and Angle properties
            rotation.Quaternion = Quaternion.CreateFromAxisAngle(Vector3.UnitX, 2f);
            Assert.AreEqual(Vector3.UnitX, rotation.Axis);
            Assert.AreEqual(2f, rotation.Angle);

            // Changing yaw, pitch, roll updates Axis and Angle properties
            q = Quaternion.CreateFromYawPitchRoll(
                MathHelper.ToRadians(10f),
                MathHelper.ToRadians(20f),
                MathHelper.ToRadians(30f));

            Vector3 axis;
            float angle;

            QuaternionExtensions.ToAxisAngle(ref q, out axis, out angle);

            rotation.Yaw = MathHelper.ToRadians(10f);
            rotation.Pitch = MathHelper.ToRadians(20f);
            rotation.Roll = MathHelper.ToRadians(30f);

            Assert.AreEqual(axis, rotation.Axis);
            Assert.AreEqual(angle, rotation.Angle);
        }

        [TestMethod]
        public void YawPitchRollPropertiesTest()
        {
            Rotation rotation = new Rotation(
                MathHelper.ToRadians(10f),
                MathHelper.ToRadians(20f),
                MathHelper.ToRadians(30f));

            // Change yaw and pitch and roll stay the same
            rotation.Yaw = MathHelper.ToRadians(40f);
            Assert.AreEqual(MathHelper.ToRadians(20f), rotation.Pitch);
            Assert.AreEqual(MathHelper.ToRadians(30f), rotation.Roll);

            // Changing quaternion updates yaw, pitch, roll
            Quaternion q = Quaternion.CreateFromYawPitchRoll(
                MathHelper.ToRadians(10f),
                MathHelper.ToRadians(20f),
                MathHelper.ToRadians(30f));

            rotation.Quaternion = q;

            Assert.AreEqual(MathHelper.ToRadians(10f), rotation.Yaw);
            Assert.AreEqual(MathHelper.ToRadians(20f), rotation.Pitch);
            Assert.AreEqual(MathHelper.ToRadians(30f), rotation.Roll);

            // Change in axis-angle updates yaw, pitch, roll
            rotation.Axis = Vector3.UnitX;
            rotation.Angle = 1f;

            q = Quaternion.CreateFromAxisAngle(Vector3.UnitX, 1f);
            float yaw, pitch, roll;
            QuaternionExtensions.ToYawPitchRoll(ref q, out yaw, out pitch, out roll);

            Assert.AreEqual(yaw, rotation.Yaw);
            Assert.AreEqual(pitch, rotation.Pitch);
            Assert.AreEqual(roll, rotation.Roll);
        }

        [TestMethod]
        public void QuaternionPropertyTest()
        {
            Rotation rotation = new Rotation();

            Quaternion q = Quaternion.CreateFromAxisAngle(Vector3.UnitX, 2f);

            // Change in axis-angle updates quaternion
            rotation.Axis = Vector3.UnitX;
            rotation.Angle = 2f;

            Assert.AreEqual(q, rotation.Quaternion);

            // Change in yaw, pitch, roll updates quaternion
            q = Quaternion.CreateFromYawPitchRoll(
                MathHelper.ToRadians(10f),
                MathHelper.ToRadians(20f),
                MathHelper.ToRadians(30f));

            rotation.Yaw = MathHelper.ToRadians(10f);
            rotation.Pitch = MathHelper.ToRadians(20f);
            rotation.Roll = MathHelper.ToRadians(30f);

            Assert.AreEqual(q, rotation.Quaternion);
        }

        [TestMethod]
        public void RotateTest()
        {
            Quaternion q = Quaternion.CreateFromYawPitchRoll(
                MathHelper.ToRadians(10f),
                MathHelper.ToRadians(20f),
                MathHelper.ToRadians(30f));

            // Rotation by rotation object
            Rotation rotation = new Rotation(q);
            rotation.Rotate(rotation);
            Assert.AreEqual(q * q, rotation.Quaternion);

            // Rotation by quaternion
            rotation = new Rotation(q);
            rotation.Rotate(q);
            Assert.AreEqual(q * q, rotation.Quaternion);

            // Rotation by axis-angle
            rotation = new Rotation(q);
            rotation.Rotate(Vector3.UnitX, 2f);

            Quaternion r = Quaternion.CreateFromAxisAngle(Vector3.UnitX, 2f);
            Assert.AreEqual(q * r, rotation.Quaternion);
        }

        [TestMethod]
        public void SetIdentityTest()
        {
            Rotation rotation = new Rotation(
                MathHelper.ToRadians(10),
                MathHelper.ToRadians(20),
                MathHelper.ToRadians(30));

            rotation.SetIdentity();

            Assert.AreEqual(Quaternion.Identity, rotation.Quaternion);
        }
    }
}
